/**
 * @fileOverview
 * @author iNahoo
 * @since 2019/12/18.
 */

const BaseBitMap = require('./BaseBitMap');

/**
 * @param a {Matrix}
 * @param b {Matrix}
 */
function assertType(a, b) {
    const ta = a.Type;
    const tb = b.Type;
    if (ta !== tb) {
        throw TypeError(`TypeArray must match, but now one is ${ta.name}, the other is ${tb.name}`);
    }
}

class Matrix extends BaseBitMap {
    constructor(bitmap, w, h) {
        if (bitmap.length !== w * h) {
            throw TypeError(`Matrix Size Error! expect ${w}*${h}=${w * h} , got ${bitmap.length}`);
        }

        super(bitmap);
        this.w = w;
        this.h = h;
    }

    get(i, j) {
        const _in = i * this.w + j;
        return this.bitmap[_in];
    }

    patch(i, j, v) {
        const _in = i * this.w + j;
        this.bitmap[_in] = v;
        return this;
    }

    rowApply(r, fn) {
        for (let i = 0; i < this.w; i++) {
            let s = r * this.w + i;
            this.bitmap[s] = fn(this.bitmap[s]);
        }
        return this;
    }

    colApply(c, fn) {
        for (let i = 0; i < this.h; i++) {
            let s = i * this.w + c;
            this.bitmap[s] = fn(this.bitmap[s]);
        }
        return this;
    }

    log() {
        for (let i = 0; i < this.h; i++) {
            let s = i * this.w;
            console.log(this.bitmap.slice(s, s + this.w).toString());
        }
    }

    /**
     * 矩阵裁剪(降维)
     * @param t
     * @param l
     * @param b
     * @param r
     */
    slice(t = 0, l = 0, b = 0, r = 0) {
        const Type = this.Type;
        const w = this.w - l - r;
        const h = this.h - t - b;
        const typeArr = new Type(w * h);

        const _w = this.w - r;
        const _h = this.h - b;

        let q1 = 0;
        let q2 = l + t * this.w;

        for (let i = t; i < _h; i++) {
            for (let j = l; j < _w; j++) {
                typeArr[q1] = this.bitmap[q2];
                q1++;
                q2++;
            }

            q2 += l + r;
        }

        return new Matrix(typeArr, w, h);
    }

    /**
     * 矩阵相乘
     * @param a {Matrix}
     * @param b {Matrix}
     */
    static multiplication(a, b) {
        assertType(a, b);

        if (a.w !== b.h) {
            throw new TypeError()
        }

        const p = a.w;

        const bit = new a.Type(b.w * a.h);
        const c = new Matrix(bit, b.w, a.h);

        function getSum(i, j) {
            let s = 0;
            for (let k = 0; k < p; k++) {
                s += a.get(i, k) * b.get(k, j);
            }
            return s;
        }

        for (let i = 0; i < a.h; i++) {
            for (let j = 0; j < b.w; j++) {
                bit[i * b.w + j] = getSum(i, j);
            }
        }

        return c;
    }

    static DiagonalMatrix(TypedArr) {
        const Type = Object.getPrototypeOf(TypedArr).constructor;
        const L = TypedArr.length;

        const arr = new Type(L * L);

        if (arr instanceof Array) {
            arr.fill(0);
        }

        for (let i = 0; i < L; i++) {
            arr[i * L + i] = TypedArr[i];
        }

        return new Matrix(arr, L, L);
    }

    static add(a, b) {
        assertType(a, b);

    }

    /**
     * 求行列式
     * @param a {Matrix}
     */
    static det(a) {

    }
}

module.exports = Matrix;